//
// Created by 33145 on 2023/3/29.
//

#ifndef SYLAR_WS_SESSION_H
#define SYLAR_WS_SESSION_H
#include "sylar/config.h"
#include "sylar/http/http_session.h"
#include <cstdint>

namespace sylar {
  namespace http {

    /// 告诉编译器这段代码的对齐格式
    /// Frame format:
    ///     @param opcode [0~2] 0x0 表示延续，0x1 表示文本 (总是用 UTF-8 编码)，0x2 表示二进制 [3~7] 无意义 [9~10] PING PONG
    ///     @param fin    [0~1] 0:表示后序还有消息到来 1：这是最后一条消息，可以结束接收了
    ///     @param payload_len 7位，解析为无符号整型：长度<= 125 直接按长度接收，126 读取接下来的16位，127 读取接下来的64位
    ///     @param mask   1 读取接下来的4字节（Masking-key）
    ///      0                   1                   2                   3
    ///      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    ///     +-+-+-+-+-------+-+-------------+-------------------------------+
    ///     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
    ///     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
    ///     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
    ///     | |1|2|3|       |K|             |                               |
    ///     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
    ///     |     Extended payload length continued, if payload len == 127  |
    ///     + - - - - - - - - - - - - - - - +-------------------------------+
    ///     |                               |Masking-key, if MASK set to 1  |
    ///     +-------------------------------+-------------------------------+
    ///     | Masking-key (continued)       |          Payload Data         |
    ///     +-------------------------------- - - - - - - - - - - - - - - - +
    ///     :                     Payload Data continued ...                :
    ///     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
    ///     |                     Payload Data continued ...                |
    ///     +---------------------------------------------------------------+
#pragma pack(1)
    struct WSFrameHead {
      enum OPCODE {
        /// 数据分片帧
        CONTINUE = 0,
        /// 文本帧
        TEXT_FRAME = 1,
        /// 二进制帧
        BIN_FRAME = 2,
        /// 断开连接
        CLOSE = 8,
        /// PING
        PING = 0x9,
        /// PONG
        PONG = 0xA
      };
      uint32_t opcode: 4;
      bool rsv3: 1;
      bool rsv2: 1;
      bool rsv1: 1;
      bool fin: 1;
      uint32_t payload: 7;
      bool mask: 1;

      std::string toString() const;
    };
#pragma pack()

    class WSFrameMessage {
    public:
      typedef std::shared_ptr<WSFrameMessage> ptr;
      WSFrameMessage(int opcode = 0, const std::string& data = "");

      int getOpcode() const { return m_opcode;}
      void setOpcode(int v) { m_opcode = v;}

      const std::string& getData() const { return m_data;}
      std::string& getData() { return m_data;}
      void setData(const std::string& v) { m_data = v;}
    private:
      int m_opcode;
      std::string m_data;
    };

    class WSSession : public HttpSession {
    public:
      typedef std::shared_ptr<WSSession> ptr;
      WSSession(Socket::ptr sock, bool owner = true);

      /// server client
      HttpRequest::ptr handleShake();

      WSFrameMessage::ptr recvMessage();
      int32_t sendMessage(WSFrameMessage::ptr msg, bool fin = true);
      int32_t sendMessage(const std::string& msg, int32_t opcode = WSFrameHead::TEXT_FRAME, bool fin = true);
      int32_t ping();
      int32_t pong();
    private:
      bool handleServerShake();
      bool handleClientShake();
    };

    extern sylar::ConfigVar<uint32_t>::ptr g_websocket_message_max_size;
    WSFrameMessage::ptr WSRecvMessage(Stream* stream, bool client);
    int32_t WSSendMessage(Stream* stream, WSFrameMessage::ptr msg, bool client, bool fin);
    int32_t WSPing(Stream* stream);
    int32_t WSPong(Stream* stream);

  }
}
#endif //SYLAR_WS_SESSION_H
